home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 3 / Cream of the Crop 3.iso / comm / wnos5src.zip / NNTPCLI.C < prev    next >
C/C++ Source or Header  |  1993-10-03  |  22KB  |  1,028 lines

  1. /*
  2.  *
  3.  * NNTP Client - See RFC977
  4.  * Jeffrey R. Comstock. - NR0D - Bloomington, Minnesota USA
  5.  * Copyright 1990 Jeffrey R. Comstock, All Rights Reserved.
  6.  * Permission granted for non-commercial copying and use, provided
  7.  * this notice is retained.
  8.  *
  9.  * DB3FL 9107xx: heavily rewritten and bug fixing in file-handling
  10.  * DB3FL 920121: splitted into several files
  11.  * DB3FL 920131: included IHAVE command/offer to server
  12.  * DG1ZX 9210xx: minimizing overhead in ihave cmd and bug fixing
  13.  *
  14.  */
  15.  
  16. #include <stdio.h>
  17. #include <dos.h>
  18. #include <time.h>
  19. #include <ctype.h>
  20. #include <dir.h>
  21. #include <io.h>
  22.  
  23. #include "global.h"
  24. #include "config.h"
  25. #include "nntp.h"
  26. #include "files.h"
  27. #include "domain.h"
  28. #include "socket.h"
  29. #include "cmdparse.h"
  30. #include "session.h"
  31. #include "netuser.h"
  32. #ifdef LZW
  33. #include "lzw.h"
  34. #endif
  35. #include "clients.h"
  36.  
  37.  
  38. static char quitcmd[]      = "QUIT\n";
  39.  
  40. struct post Post;
  41.  
  42. static int16 NnIhave = 0;
  43.  
  44. #undef NNTP_LT                    /* not yet used */
  45.  
  46. #ifdef NNTP_LT
  47. static int Nntp_lifetime = 30;    /* 30 days of lifetime per default */
  48. #endif
  49.  
  50. /* handles the response code of an incoming msg
  51.  * returncode: -1 error; 0 no code; value of response code on success */
  52. static int near
  53. getreply(struct nntpserv *cb)
  54. {
  55.     int response;
  56.     char *cp;
  57.  
  58.     while(recvline(cb->s,cb->buf,LineLen) != -1) {
  59.         /* skip informative messages and blank lines */
  60.         if(*cb->buf == '\0' || *cb->buf == '1') {
  61.             continue;
  62.         }
  63.         if((cp = strchr(cb->buf,' ')) != NULLCHAR) {
  64.             *cp = '\0';
  65.             response = atoi(cb->buf);
  66.             *cp = ' ';
  67.         } else {
  68.             return -1;
  69.         }
  70.         return response;
  71.     }
  72.     return -1;
  73. }
  74.  
  75. /*
  76.  *  checks id from IHAVE offer against existing articles id received by
  77.  *  newnews command. When this test passed, checks if one of the hostname
  78.  *  in pathfield is identical with hostname, we are polling now. (DG1ZX)
  79.  *
  80.  *  return-code: 0 = no such article         ==> IHAVE ok
  81.  *                 1 = article already exists ==> don't offer
  82.  *
  83.  */
  84. static int near
  85. check_ihave(FILE *fp,char *id,struct nntpserv *mp,char *hostn)
  86. {
  87.     char *p = strchr(id,'<');
  88.  
  89.     /* checks if article is offered in this session from host */
  90.     rewind(fp);
  91.  
  92.     for (;;) {
  93.         if(fgets(mp->buf,LineLen,fp) == NULL) {
  94.             break;
  95.         }
  96.         if(strstr(mp->buf,p) != NULL) {
  97.             return 1;
  98.         }
  99.     }
  100.     if(hostn == NULLCHAR) {
  101.         return 0;
  102.     }
  103.     /* now we check the pathfield of this article */
  104.     strcpy(mp->buf,id);
  105.     xfree(id);
  106.  
  107.     p = strchr(mp->buf,'>') + 1;
  108.     *p = '\0';                            /* cut off CR LF ! */
  109.  
  110.     return (doarticle(mp,0,hostn) ? 0 : 1);
  111. }
  112.  
  113. static char * near
  114. pollpos(char *dest,FILE *f)
  115. {
  116.     int32 t;
  117.     char *cp, line[LineLen];
  118.  
  119.     rewind(f);
  120.  
  121.     for(t = 0L; fgets(line,LineLen,f) != NULL; t = ftell(f)) {
  122.         if((cp = strchr(line,' ')) == NULLCHAR) {
  123.             /* something wrong with this line, skip it */
  124.             continue;
  125.         }
  126.         *cp = '\0';
  127.         if(strcmp(line,dest) == 0) {
  128.             fseek(f,t,SEEK_SET);
  129.             return cp + 1;
  130.         }
  131.     }
  132.     return 0;
  133. }
  134.  
  135. static void
  136. nntppoll(int unused,void *cb1,void *p)
  137. {
  138.     char *cp;
  139.     struct sockaddr_in fsocket;
  140.     struct nntpserv *cb;
  141.     FILE *f = NULLFILE, *f1 = NULLFILE, *pf = NULLFILE;
  142.     int err = 0, r, ret;
  143.     int32 SavePollTime;
  144.     struct tm *ltm;
  145.     struct Server *np = (struct Server *)cb1;
  146.  
  147.     if (!Filecheck)
  148.         if(check_system())
  149.             return;
  150.  
  151.     if(availmem() == 0 || (pf = Fopen(Poll,"r+",0,1)) == NULLFILE)
  152.         return;
  153.  
  154.     np->busy = TRUE;
  155.  
  156.     cb = mxallocw(sizeof(struct nntpserv));
  157.     cb->buf = mxallocw(LINELEN);
  158.  
  159.     SavePollTime = currtime;
  160.  
  161.     /* if no entry for host in poll-file exist, set date to yesterday (DG1ZX) */
  162.     if((cp = pollpos(np->name,pf)) == NULLCHAR) {
  163.         int32 lastday = SavePollTime - DAYS;
  164.         ltm = gmtime(&lastday);
  165.  
  166.         sprintf(cb->newnews,"%02d%02d%02d %02d%02d%02d",
  167.             ltm->tm_year,ltm->tm_mon + 1,ltm->tm_mday,
  168.             ltm->tm_hour,ltm->tm_min,ltm->tm_sec);
  169.     } else {
  170.         cb->newnews = strxdup(cp);
  171.     }
  172.     rip(cb->newnews);
  173.     Fclose(pf);
  174.  
  175.     fsocket.sin_family = AF_INET;
  176.     fsocket.sin_addr.s_addr = np->address;
  177.     fsocket.sin_port = IPPORT_NNTP;
  178.  
  179.     if((cb->s = socket(AF_INET,SOCK_STREAM,0)) == -1) {
  180.         goto quit;
  181.     }
  182.     sockmode(cb->s,SOCK_ASCII);
  183.  
  184.     if(connect(cb->s,(char *)&fsocket,SOCKSIZE) == -1) {
  185.         goto quit;
  186.     }
  187.     log(cb->s,9983,"NNTP Poll");
  188.  
  189.     if(getreply(cb) == -1) {     /* throw away any hello msg */
  190.         goto quit;
  191.     }
  192. #ifdef LZW
  193.     if(LzwActive) {
  194.         usprintf(cb->s,"XLZW %d %d\n",Lzwbits,Lzwmode);
  195.         if((ret = getreply(cb)) == 235)   {       /* eat negative response */
  196.             lzwinit(cb->s,Lzwbits,Lzwmode);
  197.         }
  198.     }
  199. #endif
  200.  
  201.     usputs(cb->s,"SLAVE\n");
  202.  
  203.     if(getreply(cb) != 202) {
  204.         goto quit;
  205.     }
  206.     if((f = Tmpfile(0,1)) == NULLFILE) {
  207.         goto quit;
  208.     }
  209.     cb->states = N_SLAVE;
  210.  
  211.     usprintf(cb->s,"NEWNEWS %s %s\n",np->arg1,cb->newnews);
  212.  
  213.     if(getreply(cb) != 230) {
  214.         goto quit1;
  215.     }
  216.     if(recv_file(f,cb->s) == -1) {
  217.         goto quit1;
  218.     }
  219.     if((f1 = Tmpfile(cb->s,1)) == NULLFILE) {
  220.         goto quit1;
  221.     }
  222.     rewind(f);
  223.  
  224.     while(fgets(cb->buf,LineLen,f) != NULL) {
  225.         rip(cb->buf);
  226.         if(strcmp(cb->buf,".") == 0) {
  227.             break;
  228.         }
  229.         if(check_article(cb->buf) == 1) {
  230.             continue;
  231.         }
  232.         usprintf(cb->s,"ARTICLE %s\n",cb->buf);
  233.         for (;;) {
  234.             if((r = recvline(cb->s,cb->buf,LineLen)) == -1) {
  235.                 break;
  236.             }
  237.             rip(cb->buf);
  238.             if(!isdigit(cb->buf[0])) {
  239.                 r = -1;
  240.                 continue;
  241.             } else {
  242.                 r = atoi(cb->buf);
  243.                 break;
  244.             }
  245.         }
  246.         if(r == -1) {
  247.             Fclose(f1);
  248.             goto quit1;
  249.         }
  250.         if(r == 220) {
  251.             if(recv_file(f1,cb->s) == -1) {
  252.                 Fclose(f1);
  253.                 goto quit1;
  254.             }
  255.             rewind(f1);
  256.             while(fgets(cb->buf,LineLen,f1) != NULL) {
  257.                 rip(cb->buf);
  258.                 if (strnicmp(cb->buf,Hdrs[MSGID],12) == 0) {
  259.                     cp = strchr(cb->buf,' ');
  260.                     cb->id = strxdup((*++cp < 32) ? "(none)" : cp);
  261.                     break;
  262.                 }
  263.             }
  264.             /* minimum header in article required !
  265.              * Now check again, if same news exists in history (DG1ZX) */
  266.             if (garbled(f1) == 0 && check_article(cb->id) == 0) {
  267.                 rewind(f1);
  268.                 xfer_article2(f1,cb);
  269.             }
  270.         }
  271.         Fclose(f1);
  272.         if ((f1 = Tmpfile(cb->s,1)) == NULLFILE) {
  273.             goto quit1;
  274.         }
  275.     }
  276.     Fclose(f1);
  277.  
  278.     /* IHAVE offer */
  279.     if(NnIhave && (f1 = Tmpfile(0,0)) != NULLFILE) {
  280.         sprintf(cb->buf,"%s %s",NnIhave == 2 ? "*" : np->arg1,cb->newnews);
  281.         if(newnews(cb,f1) == 1) {
  282.             rewind(f1);
  283.             while(fgets(cb->buf,LINELEN,f1) != NULL) {
  284.                 /* minimize overhead (DG1ZX) */
  285.                 char *cp = strxdup(cb->buf);
  286.                 if(check_ihave(f,cp,cb,np->name)) {
  287.                     continue;
  288.                 }
  289.                 usprintf(cb->s,"IHAVE %s",cb->buf);
  290.                 if((ret = getreply(cb)) == -1) {
  291.                     err = 1;
  292.                     break;
  293.                 }
  294.                 if(ret != 335) {
  295.                     continue;
  296.                 }
  297.                 rip(cb->buf);
  298.  
  299.                 if(doarticle(cb,0,NULLCHAR) < 1) {
  300.                     usputs(cb->s,NEol);
  301.                     if((ret = getreply(cb)) == -1) {
  302.                         err = 1;
  303.                         break;
  304.                     }
  305.                     continue;
  306.                 }
  307.                 if((ret = getreply(cb)) != 235) {
  308.                     continue;
  309.                 }
  310.             }
  311.         }
  312.         Fclose(f1);
  313.     }
  314.     /*
  315.      * update pollfile
  316.      */
  317.     if(!err && (pf = Fopen(Poll,"r+",0,0)) != NULLFILE) {
  318.         struct tm *ltm;
  319.  
  320.         pollpos(np->name,pf);
  321.         ltm = gmtime(&SavePollTime);
  322.  
  323.         /* now write back the name of the server, date and time */
  324.         fprintf(pf,"%s %02d%02d%02d %02d%02d%02d\n",
  325.             np->name,
  326.             ltm->tm_year,ltm->tm_mon + 1,ltm->tm_mday,
  327.             ltm->tm_hour,ltm->tm_min,ltm->tm_sec);
  328.  
  329.         Fclose(pf);
  330.     }
  331.  
  332. quit1:
  333.     Fclose(f);
  334. quit:
  335.     usputs(cb->s,quitcmd);
  336.     if(getreply(cb) == -1) ;
  337.     xfree(cb->newnews);
  338.  
  339.     if((cp = sockerr(cb->s)) == NULLCHAR) {
  340.         cp = "EOF";
  341.     }
  342.     log(cb->s,9983,"NNTP closed %s",cp);
  343.  
  344.     close_s(cb->s);
  345.     xfree(cb->buf);
  346.     xfree(cb);
  347.     np->busy = FALSE;
  348. }
  349.  
  350. static char * near
  351. input_line(char *msg,struct session *sp)
  352. {
  353.     static char buf[LineLen];
  354.  
  355.     for (;;) {
  356.         usputs(sp->output,msg);
  357.         usflush(sp->output);
  358.  
  359.         if(recvline(sp->input,buf,LineLen) == -1) {
  360.             return NULLCHAR;
  361.         }
  362.         rip(buf);
  363.  
  364.         if(!check_blank(buf)){
  365.             return buf;
  366.         }
  367.     }
  368. }
  369.  
  370. /* ---------------------- NNTP Client subcmds ----------------------- */
  371.  
  372. /* lists active newsgroups
  373.  * returncode: -1 if error; 0 success */
  374. static int
  375. donnactive(int argc,char **argv,void *p)
  376. {
  377.     FILE *fp;
  378.     char line[LINELEN], *cp;
  379.  
  380.     if((fp = Fopen(Active,READ_TEXT,0,1)) != NULLFILE) {
  381.         tputs("Msg#  next  post newsgroups\n");
  382.  
  383.         while(fgets(line,LINELEN,fp) != NULL) {
  384.             if((cp = strchr(line,' ')) != NULLCHAR) {
  385.                 *cp = '\0';
  386.                 rip(++cp);
  387.                 tprintf("%s    %s\n",cp,line);
  388.             }
  389.         }
  390.         Fclose(fp);
  391.         return 0;
  392.     }
  393.     return -1;
  394. }
  395.  
  396. /* add nntp servers to list */
  397. static int
  398. donnadds(int argc,char **argv,void *p)
  399. {
  400.     struct Server *np;
  401.     int i;
  402.  
  403.     if((np = addserver(IPPORT_NNTP,argv[1])) == NULLSERVER)
  404.         return -1;
  405.  
  406.     np->arg1 = mxallocw(LineLen);
  407.  
  408.     for (i = 2; i < argc; i++) {
  409.         if((strlen(np->arg1) + strlen(argv[i]) + 2) >= LineLen) {
  410.             tprintf("To many groups, '%s' ignored\n", argv[i]);
  411.         } else {
  412.             /* it's a group, and it fits... add it to list */
  413.             if (*np->arg1 != '\0')
  414.                 strcat(np->arg1, ",");
  415.             strcat(np->arg1,argv[i]);
  416.         }
  417.     }
  418.     return 0;
  419. }
  420.  
  421. #ifdef NNTPENH
  422. /*
  423.     This command will control, how incoming articles are processed.
  424.     If fullauto flag is set, the nntp control files are updated to include
  425.     the new group and the subdirectories are automaticly generated.
  426.     Otherwise, all news that are not in the active file will go to
  427.     \spool\news\junk (DG1ZX).
  428.  
  429.     Syntax: nntp config <yes|no>
  430.     Default: nntp config yes
  431. */
  432. static int
  433. donnconfig(int argc,char **argv,void *p)
  434. {
  435.     return setbool(&fullauto,"NNTP AutoConfig",argc,argv);
  436. }
  437. #endif
  438.  
  439. #ifdef NNTPENH
  440. /*
  441.    This command will add a new newsgroup to the filesystem. (DG1ZX)
  442.  
  443.    Syntax: nntp create <newsgroup>
  444.    Example: nntp create ampr.news.nrn.sources
  445. */
  446. static int
  447. donncreate(int argc,char **argv,void *p)
  448. {
  449.     FILE *f;
  450.     char line[LineLen];
  451.     int update_cntrl = 0;
  452.  
  453.     if((f = Fopen(Pointer,READ_TEXT,0,1)) == NULLFILE) {
  454.         return -1;
  455.     }
  456.     for (;;) {
  457.         if(fgets(line,LineLen,f) == NULL) {
  458.             /* update pointerfile */
  459.             update_cntrl = 1;
  460.             break;
  461.         }
  462.         if(strcspn(line," ") != strlen(argv[1])) {
  463.             continue;
  464.         }
  465.         if(strnicmp(argv[1],line,strlen(argv[1])) == 0) {
  466.             /* newsgroup in pointerfile exists */
  467.             break;
  468.         }
  469.     }
  470.     Fclose(f);
  471.  
  472.     /* creating path to this newsgroup */
  473.     if(make_path(argv[1],update_cntrl)) {
  474.         return -1;
  475.     }
  476.     if((f = Fopen(Active,APPEND_TEXT,0,0)) == NULLFILE) {
  477.         return -1;
  478.     }
  479.     for (;;) {
  480.         if(fgets(line,LineLen,f) == NULL) {
  481.             /* update active file */
  482.             fprintf(f,"%s 00000 00001 y\n",argv[1]);
  483.             break;
  484.         }
  485.         if (strcspn(line," ") != strlen(argv[1])) {
  486.             continue;
  487.         }
  488.         if (strnicmp(argv[1],line,strlen(argv[1])) == 0) {
  489.             /* newsgroup in active file exists */
  490.             break;
  491.         }
  492.     }
  493.     Fclose(f);
  494.     return 0;
  495. }
  496. #endif
  497.  
  498. /* drops nntp servers from list */
  499. static int
  500. donndrops(int argc,char **argv,void *p)
  501. {
  502.     dropserver(IPPORT_NNTP,argv[1]);
  503.     return 0;
  504. }
  505.  
  506. #ifdef XXX
  507. /* copies a news from given newsgroup to the mailbox */
  508. static int
  509. donndump(int argc,char **argv,void *p)
  510. {
  511.     FILE *t, *f, *o;
  512.     char line[LineLen], newsname[10], *cp;
  513.     struct ffblk blk;
  514.  
  515.     struct article *art = mxallocw(sizeof(struct article));
  516.  
  517.     art->group = strxdup(argv[1]);
  518.  
  519.     if(get_path2(art) < 1)
  520.         goto error2;
  521.  
  522.     rip(art->path);
  523.     sprintf(line,"%s/*.*",art->path);
  524.  
  525.     if(findfirst(line,&blk,0)) {
  526.         tputs("No news in newsgroup\n");
  527.         goto error;
  528.     }
  529.  
  530.     sprintf(newsname,"%.8s",argv[2]);
  531.     sprintf(line,"%s/%s.txt",Mailspool,newsname);
  532.  
  533.     if((o = Fopen(line,"a+",0,1)) == NULLFILE)
  534.         goto error;
  535.     if(!(mlock(Mailspool,newsname))) {
  536.         tprintf("Newsgroup dump to %s\n",line);
  537.         for (;;) {
  538.             if((t = Tmpfile(0,1)) == NULLFILE) {
  539.                 Fclose(o);
  540.                 goto error;
  541.             }
  542.             sprintf(line,"%s/%s",art->path,blk.ff_name);
  543.             /* Open the article */
  544.             if ((f = Fopen(line,READ_TEXT,0,1)) == NULLFILE) {
  545.                 Fclose(t);
  546.                 Fclose(o);
  547.                 goto error;
  548.             }
  549.             pwait(NULL);
  550.             tputs(".");     /* One article/dot processed */
  551.             tflush();
  552.  
  553.             while(fgets(line,LineLen,f) != NULL) {
  554.                 fputs(line,t);
  555.                 if (!strnicmp(line,frm,6)) {
  556.                     cp = strchr(line,' ') + 1;
  557.                     fprintf(o,"From %s",cp);
  558.                 }
  559.             }
  560.             rewind(t);
  561.             while(fgets(line,LineLen,t) != NULL)
  562.                 fputs(line,o);
  563.  
  564.             fputc('\n',o);
  565.             Fclose(t);
  566.             Fclose(f);
  567.             if (findnext(&blk))
  568.                 break;
  569.         }
  570.         rmlock(Mailspool,newsname);
  571.     } else
  572.         tputs("Mailfile is busy, try later");
  573.  
  574.     Fclose(o);
  575.     tputs("\n");
  576.  
  577. error:
  578.     xfree(art->path);
  579. error2:
  580.     xfree(art->group);
  581.     xfree(art);
  582.     return 0;
  583. }
  584. #endif
  585.  
  586. static int
  587. donnfull(int argc,char **argv,void *p)
  588. {
  589.     if(argc < 2) {
  590.         if(Post.fullname != NULLCHAR)
  591.             tprintf("%s\n",Post.fullname);
  592.     } else {
  593.         if(Post.fullname != NULLCHAR)
  594.             xfree(Post.fullname);
  595.         Post.fullname = strxdup(argv[1]);
  596.     }
  597.     return 0;
  598. }
  599.  
  600. static int
  601. donnhost(int argc,char **argv,void *p)
  602. {
  603.     if(argc < 2) {
  604.         if(Post.host != NULLCHAR)
  605.             tprintf("%s\n",Post.host);
  606.     } else {
  607.         if(Post.host != NULLCHAR)
  608.             xfree(Post.host);
  609.         Post.host = strxdup(argv[1]);
  610.     }
  611.     return 0;
  612. }
  613.  
  614. static int
  615. donnihave(int argc,char **argv,void *p)
  616. {
  617.     return setintrc(&NnIhave,"NNTP Ihave",argc,argv,0,2);
  618. }
  619.  
  620. static int
  621. donnkick(int argc,char **argv,void *p)
  622. {
  623.     int32 addr = 0;
  624.     struct Server *np;
  625.  
  626.     if(argc > 1 && (addr = resolve(argv[1])) == 0) {
  627.         tprintf(Badhost,argv[1]);
  628.         return 1;
  629.     }
  630.     for(np = Server; np != NULLSERVER; np = np->next) {
  631.         if(np->protocol != IPPORT_NNTP
  632.           || np->busy
  633.           || (addr && np->address != addr)) {
  634.             continue;
  635.         } else {
  636.             newproc("NNTP Client",1280,nntppoll,0,(void *)np,0,0);
  637.         }
  638.     }
  639.     return 0;
  640. }
  641.  
  642. #ifdef NNTP_LT
  643. static int
  644. donnlifetime (int argc,char **argv,void *p)
  645. {
  646.     return setint(&Nntp_lifetime,"NNTP Lifetime",argc,argv);
  647. }
  648. #endif
  649.  
  650. /* list nntp servers */
  651. static int
  652. donnlists(int argc,char **argv,void *p)
  653. {
  654.     char *a = "Newsgroups";
  655.  
  656.     listserver(IPPORT_NNTP,argv[1],a);
  657.     return 0;
  658. }
  659.  
  660. #ifdef LZW
  661. /* sets LzwActive flag */
  662. static int
  663. donnlzw(int argc,char **argv,void *p)
  664. {
  665.     return setbool(&LzwActive,"NNTP LZW",argc,argv);
  666. }
  667. #endif
  668.  
  669. static int
  670. donnmaxcli(int argc,char **argv,void *p)
  671. {
  672.     return setshort(&Nntpmaxcli,"NNTP maxcli",argc,argv);
  673. }
  674.  
  675. static int
  676. donnorgan(int argc,char **argv,void *p)
  677. {
  678.     if(argc < 2) {
  679.         if(Post.organ != NULLCHAR)
  680.             tprintf("%s\n",Post.organ);
  681.     } else {
  682.         if(Post.organ != NULLCHAR)
  683.             xfree(Post.organ);
  684.         Post.organ = strxdup(argv[1]);
  685.     }
  686.     return 0;
  687. }
  688.  
  689. /* manually entering new news
  690.  * returncode: -1 if error; 0 success */
  691. static int
  692. donnpost(int argc,char **argv,void *p)
  693. {
  694.     struct session *sp;
  695.     struct nntpserv *mp;
  696.     char buf[LineLen], *cp;
  697.     long id;
  698. #ifdef NNTP_LT
  699.     int32 tmptime;
  700. #endif
  701.     FILE *f, *idf, *ufp;
  702.  
  703.     if (!Filecheck)
  704.         if(check_system())
  705.             return -1;
  706.  
  707.     if((sp = newsession("NNTP Post",MORE,SWAP)) == NULLSESSION) {
  708.         tputs(Nosess);
  709.         return -1;
  710.     }
  711.     mp = mxallocw(sizeof(struct nntpserv));
  712.     mp->buf = mxallocw(LINELEN);
  713.  
  714.     for (;;) {
  715.         if ((f = Tmpfile(0,1)) == NULLFILE) {
  716.             goto done;
  717.         }
  718.         if (Post.user == NULLCHAR) {
  719.             Post.user = strxdup(input_line("User name? ",sp));
  720.         }
  721.         fprintf(f,"%s%s\n",Hdrs[PATH],Post.user);
  722.         fprintf(f,"%s%s@%s",Hdrs[FROM],Post.user,Hostname);
  723.  
  724.         if (Post.fullname == NULLCHAR) {
  725.             Post.fullname = strxdup(input_line("Fullname? ",sp));
  726.         }
  727.         fprintf(f," (%s)\n",Post.fullname);
  728.  
  729.         fprintf(f,"%s%s\n",Hdrs[NEWSGROUPS],input_line("Newsgroup? ",sp));
  730.         fprintf(f,"%s%s\n",Hdrs[SUBJECT],input_line("Subject? ",sp));
  731.  
  732.         id = get_msgid();
  733.         fprintf(f,"%s<%ld@%s>\n",Hdrs[MSGID],id,Hostname);
  734.         fprintf(f,"%s%s",Hdrs[DATE],rfc822_date(&currtime));
  735. #ifdef NNTP_LT
  736.         tmptime = currtime + DAYS * Nntp_lifetime;
  737.         fprintf(f,"%s%s",Hdrs[EXPIRE]rfc822_date(&tmptime));
  738. #endif
  739.         fprintf(f,"Sender: NNTP@%s\n",Hostname);
  740.  
  741.         if (Post.reply != NULLCHAR) {
  742.             fprintf(f,"%s%s\n",Hdrs[REPLYTO],Post.reply);
  743.         }
  744.         if (Post.organ != NULLCHAR) {
  745.             fprintf(f,"%s%s\n",Hdrs[ORGANIZATION],Post.organ);
  746.         }
  747.         fputc('\n',f);
  748.         tputs("Enter message - end with .\n");
  749.  
  750.         for (;;) {
  751.             if(recvline(sp->input,buf,LineLen) == -1) {
  752.                 break;
  753.             }
  754.             if(strcmp(buf,".u\n") == 0
  755.               || strcmp(buf,".r\n") == 0) {
  756.                 tputs("Filename? ");
  757.                 if(recvline(sp->input,buf,LineLen) == -1) {
  758.                     break;
  759.                 }
  760.                 rip(buf);
  761.                 if(*buf != '\0'
  762.                   && (ufp = Fopen(buf,READ_TEXT,0,1)) != NULLFILE) {
  763.                     while(fgets(buf,LineLen,ufp) != NULL) {
  764.                         fputs(buf,f);
  765.                     }
  766.                     Fclose(ufp);
  767.                 }
  768.                 tputs("(continue)\n");
  769.             }
  770.             if(*buf == '\032'
  771.               || strcmp(buf,".\n") == 0
  772.               || strcmpi(buf,"***END\n") == 0
  773.               || strcmpi(buf,"/EX\n") == 0) {
  774.                 break;
  775.             }
  776.             fputs(buf,f);
  777.         }
  778.         if (Post.sig != NULLCHAR) {
  779.             if ((idf = Fopen(Post.sig,READ_TEXT,0,0)) != NULLFILE ) {
  780.                 while(fgets(buf,LineLen,idf) != NULL) {
  781.                     fputs(buf,f);
  782.                 }
  783.                 Fclose(idf);
  784.                 tputs("(Sig-file added)\n");
  785.             }
  786.         }
  787.         tputs("\n");
  788.  
  789. loop:   cp = input_line("[Send, Abort, Exit, List] ",sp);
  790.         switch(tolower(*cp)) {
  791.         case 's':
  792.             rewind(f);
  793.             sprintf(mp->buf,"<%ld@%s>",id,Hostname);
  794.             mp->id = strxdup(mp->buf);
  795.             xfer_article2(f,mp);
  796.             break;
  797.         case 'l':
  798.             rewind(f);
  799.             while(fgets(buf,LineLen,f) != NULL)
  800.                 tputs(buf);
  801.             rewind(f);
  802.             goto loop;
  803.         case 'e':
  804.             Fclose(f);
  805.             goto done;
  806.         case 'a':
  807.             break;
  808.         default:
  809.             goto loop;
  810.         }
  811.         Fclose(f);
  812.         cp = input_line("Post another? (y/n) ",sp);
  813.         if(*cp == 'n' || *cp == 'N') {
  814.             goto done;
  815.         }
  816.     }
  817. done:
  818.     keywait(NULLCHAR,1);
  819.     xfree(mp->buf);
  820.     xfree(mp);
  821.     freesession(sp);
  822.     return 0;
  823. }
  824.  
  825. #ifdef POST_ENBL
  826. static int
  827. dopostok(int argc,char **argv,void *p)
  828. {
  829.     return setbool(&postingok,"NNTP Posting",argc,argv);
  830. }
  831. #endif
  832.  
  833. static int
  834. donnquiet(int argc,char **argv,void *p)
  835. {
  836.     return setintrc(&Nntpquiet,"NNTP quiet",argc,argv,0,3);
  837. }
  838.  
  839. static int
  840. donnread(int argc,char **argv,void *p)
  841. {
  842.     FILE *f;
  843.     struct session *sp;
  844.     char cp[LINELEN], buf[LINELEN];
  845.     int number, row, flag = argc;
  846.  
  847.     struct article *art = mxallocw(sizeof(struct article));
  848.  
  849.     art->group = strxdup(argv[1]);
  850.  
  851.     if(get_path2(art) == 1) {
  852.         if(argc > 2) {
  853.             number = atoi(argv[2]);
  854.         } else
  855.             number = 1;
  856.  
  857.         sprintf(cp,"%s/news.rc",art->path);
  858.         if(flag < 3 && (f = Fopen(cp,READ_TEXT,0,0)) != NULLFILE) {
  859.             if(fgets(buf,LINELEN,f) != NULL) {
  860.                 number = atoi(buf);
  861.                 number++;
  862.             }
  863.             Fclose(f);
  864.         }
  865.         if((sp = newsession("NNTP read",MORE,SWAP)) != NULLSESSION) {
  866.             for(;;) {
  867.                 if(number < 1)
  868.                     number = 1;
  869.                 sp->ttystate.echo = sp->ttystate.edit = 0;
  870.                 row = Nrows - 4;
  871.                 sprintf(cp,"%s/%d",art->path,number);
  872.  
  873.                 if((f = Fopen(cp,READ_TEXT,0,0)) != NULLFILE) {
  874.                     tprintf("Msg #%d\n",number);
  875.                     while(fgets(buf,LINELEN,f) != NULL) {
  876.                         tputs(buf);
  877.                         if(--row == 0){
  878.                             row = keywait("--More--",0);
  879.                             switch(row){
  880.                             case -1:
  881.                             case 'q':
  882.                                 Fclose(f);
  883.                                 goto done;
  884.                             case '\n':
  885.                             case '\r':
  886.                                 row = 1;
  887.                                 break;
  888.                             default:
  889.                                 row = Nrows - 3;
  890.                             }
  891.                         }
  892.                     }
  893.                     Fclose(f);
  894.                 } else {
  895.                     number--;
  896.                     tputs("No more news");
  897.                 }
  898. done:
  899.                 row = keywait("\nRead next/previous? (n/p/q)",0);
  900.                 switch(row) {
  901.                     case -1:
  902.                     case 'q':
  903.                         goto done2;
  904.                     case 'p':
  905.                         flag = 3;
  906.                         if(--number < 1)
  907.                             goto done2;
  908.                         continue;
  909.                     default:
  910.                         number++;
  911.                         continue;
  912.                 }
  913.             }
  914. done2:
  915.             if(flag < 3) {
  916.                 sprintf(cp,"%s/news.rc",art->path);
  917.                 if((f = Fopen(cp,WRITE_TEXT,0,0)) != NULLFILE) {
  918.                     sprintf(cp,"%d\n",number);
  919.                     fputs(cp,f);
  920.                     Fclose(f);
  921.                 }
  922.             }
  923.             xfree(art->path);
  924.             keywait(NULLCHAR,1);
  925.             freesession(sp);
  926.         }
  927.     } else {
  928.         tprintf("No such newsgroup %s\n",art->group);
  929.     }
  930.     xfree(art->group);
  931.     xfree(art);
  932.     return 0;
  933. }
  934.  
  935. static int
  936. donnreply(int argc,char **argv,void *p)
  937. {
  938.     if(argc < 2) {
  939.         if(Post.reply != NULLCHAR)
  940.             tprintf("%s\n",Post.reply);
  941.     } else {
  942.         if(Post.reply != NULLCHAR)
  943.             xfree(Post.reply);
  944.         Post.reply = strxdup(argv[1]);
  945.     }
  946.     return 0;
  947. }
  948.  
  949. static int
  950. donnsig(int argc,char **argv,void *p)
  951. {
  952.     char buf[80];
  953.  
  954.     if(argc < 2) {
  955.         if(Post.sig != NULLCHAR)
  956.             tprintf("%s\n",Post.sig);
  957.     } else if(argc > 1) {
  958.         sprintf(buf,"%s/%s",Signature,argv[1]);
  959.         if(access(buf,0) == 0) {
  960.             if(Post.sig != NULLCHAR)
  961.                 xfree(Post.sig);
  962.             Post.sig = strxdup(buf);
  963.         } else {
  964.             tputs("No such signature file\n");
  965.             return -1;
  966.         }
  967.     }
  968.     return 0;
  969. }
  970.  
  971. static int
  972. donnuser(int argc,char **argv,void *p)
  973. {
  974.     if(argc < 2) {
  975.         if(Post.user != NULLCHAR)
  976.             tprintf("%s\n",Post.user);
  977.     } else {
  978.         if(Post.user != NULLCHAR)
  979.             xfree(Post.user);
  980.         Post.user = strxdup(argv[1]);
  981.     }
  982.     return 0;
  983. }
  984.  
  985. /* cmd parser */
  986. int
  987. donntp(int argc,char **argv,void *p)
  988. {
  989.     struct cmds Nntp[] = {
  990.         {"active",        donnactive,    0, 0, NULLCHAR},
  991.         {"add",            donnadds,    0, 3, "nntp add <server> <newsgroups>"},
  992. #ifdef NNTPENH
  993.         {"config",      donnconfig, 0, 0, NULLCHAR},
  994.         {"create",      donncreate, 0, 2, "nntp create <newsgroup>"},
  995. #endif
  996.         {"drop",        donndrops,    0, 2, "nntp drop <server>"},
  997. #ifdef XXX
  998.         {"dump",         donndump,     0, 3, "nntp dump <newsgroup> <mailfile>"},
  999. #endif
  1000.         {"fullname",    donnfull,    0, 0, NULLCHAR},
  1001.         {"hostname",    donnhost,    0, 0, NULLCHAR},
  1002.         {"ihave",        donnihave,    0, 0, NULLCHAR},
  1003.         {"kick",         donnkick,     0, 0, NULLCHAR},
  1004. #ifdef NNTP_LT
  1005.         {"lifetime",    donnlifetime,0,0, NULLCHAR},
  1006. #endif
  1007.         {"list",        donnlists,    0, 0, NULLCHAR},
  1008. #ifdef LZW
  1009.         {"lzw",         donnlzw,    0, 0, NULLCHAR},
  1010. #endif
  1011.         {"maxclient",    donnmaxcli,    0, 0, NULLCHAR},
  1012.         {"organ",        donnorgan,    0, 0, NULLCHAR},
  1013.         {"post",         donnpost,   2048, 0, NULLCHAR},
  1014. #ifdef POST_ENBL
  1015.         {"postok",      dopostok,   0, 0, NULLCHAR},
  1016. #endif
  1017.         {"quiet",        donnquiet,    0, 0, NULLCHAR},
  1018.         {"read",        donnread,    1280, 2, "nntp read <newsgroup> [number]"},
  1019.         {"reply",        donnreply,    0, 0, NULLCHAR},
  1020.         {"signature",    donnsig,     0, 0, NULLCHAR},
  1021.         {"user",        donnuser,    0, 0, NULLCHAR},
  1022.         NULLCHAR,
  1023.     };
  1024.  
  1025.     return (subcmd(Nntp,argc,argv,p));
  1026. }
  1027.  
  1028.